03. Request Permission
KOTLIN PART2 L4 A03 Request Permissions
Add permissions to the AndroidManifest
The Geofencing API requires location to be shared at all times. If you are on Q or later, you will need to specifically ask the user for it. You will now add in the location permissions in the AndroidManifest.xml:
- Add in permissions for
ACCESS_FINE_LOCATIONandACCESS_BACKGROUND_LOCATIONpermission above the application tag.
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
Check if the device is running Q (API 29) or later
The difference in location permissions only affects Android Q or later, so the background location permission is only needed for those devices.
- In
HuntMainActivity.kt, add a member variable above theonCreate()method calledrunningQOrLater. This will check what API the device is running.
private val runningQOrLater = android.os.Build.VERSION.SDK_INT >=
android.os.Build.VERSION_CODES.Q
Create method to check for permissions
In your app, you will need to check if permissions are granted, and if they are not, ask for the correct permissions.
In HuntMainActivity, replace the code in theforegroundAndBackgroundLocationPermissionApprovedmethod with this. Each step will be explained in the bullet points below:
@TargetApi(29)
private fun foregroundAndBackgroundLocationPermissionApproved(): Boolean {
val foregroundLocationApproved = (
PackageManager.PERMISSION_GRANTED ==
ActivityCompat.checkSelfPermission(this,
Manifest.permission.ACCESS_FINE_LOCATION))
val backgroundPermissionApproved =
if (runningQOrLater) {
PackageManager.PERMISSION_GRANTED ==
ActivityCompat.checkSelfPermission(
this, Manifest.permission.ACCESS_BACKGROUND_LOCATION
)
} else {
true
}
return foregroundLocationApproved && backgroundPermissionApproved
}
- First, check if the
ACCESS_FINE_LOCATIONpermission is granted.
val foregroundLocationApproved = (
PackageManager.PERMISSION_GRANTED ==
ActivityCompat.checkSelfPermission(this,
Manifest.permission.ACCESS_FINE_LOCATION))
- If the device is running Q or higher, check that the
ACCESS_BACKGROUND_LOCATIONpermission is granted. Return true if the device is running lower than Q where you don't need a permission to access location in the background.
val backgroundPermissionApproved =
if (runningQOrLater) {
PackageManager.PERMISSION_GRANTED ==
ActivityCompat.checkSelfPermission(
this, Manifest.permission.ACCESS_BACKGROUND_LOCATION
)
} else {
true
}
*Return true if the permissions are granted and false if not.
return foregroundLocationApproved && backgroundPermissionApproved
Request permissions
- Copy this code into the
requestForegroundAndBackgroundLocationPermissions()method, This is where you will ask the user to grant location permissions. Each step will be explained in the bullet points below:
@TargetApi(29 )
private fun requestForegroundAndBackgroundLocationPermissions() {
if (foregroundAndBackgroundLocationPermissionApproved())
return
var permissionsArray = arrayOf(Manifest.permission.ACCESS_FINE_LOCATION)
val resultCode = when {
runningQOrLater -> {
permissionsArray += Manifest.permission.ACCESS_BACKGROUND_LOCATION
REQUEST_FOREGROUND_AND_BACKGROUND_PERMISSION_RESULT_CODE
}
else -> REQUEST_FOREGROUND_ONLY_PERMISSIONS_REQUEST_CODE
}
Log.d(TAG, "Request foreground only location permission")
ActivityCompat.requestPermissions(
this@HuntMainActivity,
permissionsArray,
resultCode
)
}
- If the permissions have already been approved, you don’t need to ask again. Return out of the method.
if (foregroundAndBackgroundLocationPermissionApproved())
return
- The
permissionsArraycontains the permissions that are going to be requested. Initially, addACCESS_FINE_LOCATIONsince that will be needed on all API levels.
var permissionsArray = arrayOf(Manifest.permission.ACCESS_FINE_LOCATION)
- Below it, you will need a
resultCode. The code will be different depending on if the device is running Q or later and will inform us if you need to check for one permission (fine location) or multiple permissions (fine and background location) when the user returns from the permission request screen. Add awhenstatement to check the version running and assign result code toREQUEST_FOREGROUND_AND_BACKGROUND_PERMISSION_RESULT_CODEif the device is running Q or later andREQUEST_FOREGROUND_ONLY_PERMISSIONS_REQUEST_CODEif not.
val resultCode = when {
runningQOrLater -> {
permissionsArray += Manifest.permission.ACCESS_BACKGROUND_LOCATION
REQUEST_FOREGROUND_AND_BACKGROUND_PERMISSION_RESULT_CODE
}
else -> REQUEST_FOREGROUND_ONLY_PERMISSIONS_REQUEST_CODE
}
- Request permissions passing in the current activity, the permissions array and the result code.
ActivityCompat.requestPermissions(
this@HuntMainActivity,
permissionsArray,
resultCode
)
Handle permissions
Once the user responds to the permissions, you will need to handle their response using the onRequestPermissionsResult().
- Copy this code in under the:
override fun onRequestPermissionsResult(
requestCode: Int,
permissions: Array<String>,
grantResults: IntArray
) {
Log.d(TAG, "onRequestPermissionResult")
if (
grantResults.isEmpty() ||
grantResults[LOCATION_PERMISSION_INDEX] == PackageManager.PERMISSION_DENIED ||
(requestCode == REQUEST_FOREGROUND_AND_BACKGROUND_PERMISSION_RESULT_CODE &&
grantResults[BACKGROUND_LOCATION_PERMISSION_INDEX] ==
PackageManager.PERMISSION_DENIED))
{
Snackbar.make(
binding.activityMapsMain,
R.string.permission_denied_explanation,
Snackbar.LENGTH_INDEFINITE
)
.setAction(R.string.settings) {
startActivity(Intent().apply {
action = Settings.ACTION_APPLICATION_DETAILS_SETTINGS
data = Uri.fromParts("package", BuildConfig.APPLICATION_ID, null)
flags = Intent.FLAG_ACTIVITY_NEW_TASK
})
}.show()
} else {
checkDeviceLocationSettingsAndStartGeofence()
}
}
- Permissions can be denied in a few ways:
- If the
grantResultsarray is empty, then the interaction was interrupted and the permission request was cancelled. - If the
grantResultsarray’s value at theLOCATION_PERMISSION_INDEXhas aPERMISSION_DENIEDit means that the user denied foreground permissions. - If the request code equals
REQUEST_FOREGROUND_AND_BACKGROUND_PERMISSION_RESULT_CODEand theBACKGROUND_LOCATION_PERMISSION_INDEXis denied it means that the device is running API 29 or above and that background permissions were denied.
- If the
if (grantResults.isEmpty() ||
grantResults[LOCATION_PERMISSION_INDEX] == PackageManager.PERMISSION_DENIED ||
(requestCode == REQUEST_FOREGROUND_AND_BACKGROUND_PERMISSION_RESULT_CODE &&
grantResults[BACKGROUND_LOCATION_PERMISSION_INDEX] ==
PackageManager.PERMISSION_DENIED))
- This app has very little use when permissions are not granted so present a snackbar explaining that the user needs location permissions in order to play.
Snackbar.make(
binding.activityMapsMain,
R.string.permission_denied_explanation,
Snackbar.LENGTH_INDEFINITE
)
.setAction(R.string.settings) {
startActivity(Intent().apply {
action = Settings.ACTION_APPLICATION_DETAILS_SETTINGS
data = Uri.fromParts("package", BuildConfig.APPLICATION_ID, null)
flags = Intent.FLAG_ACTIVITY_NEW_TASK
})
}.show()
- If not, permissions have been granted! Call the
checkDeviceLocationSettingsAndStartGeofence()method!
else {
checkDeviceLocationSettingsAndStartGeofence()
}
- Run the app! You will see a pop up prompting you to grant permissions. Choose Allow all the time or Allow if you are running an API lower than 29.